package com.microframework.base.core.datasource;

import java.io.FileInputStream;
import java.sql.Connection;
import java.util.LinkedList;
import java.util.List;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import com.jfinal.json.JFinalJson;
import com.jfinal.kit.StrKit;
import com.jfinal.plugin.activerecord.ActiveRecordPlugin;
import com.jfinal.plugin.activerecord.dialect.MysqlDialect;
import com.jfinal.plugin.activerecord.dialect.OracleDialect;
import com.jfinal.plugin.activerecord.dialect.PostgreSqlDialect;
import com.jfinal.plugin.activerecord.dialect.SqlServerDialect;
import com.jfinal.plugin.activerecord.dialect.Sqlite3Dialect;
import com.jfinal.plugin.druid.DruidPlugin;
import com.microframework.base.core.datasource.properties.DbProperties;
import com.microframework.base.core.util.ReflectUtil;

import lombok.extern.slf4j.Slf4j;

@Slf4j
@Primary
@Configuration
public class JFinalConfig implements InitializingBean, DisposableBean {
	
	
//	@Value("${dev.mode}")
//    private boolean mode; 
	
	@Value("${spring.profiles.active}")
    private String active; 
	
	@Value("${spring.ds.path}")
    private String path; 

	public void destroy() throws Exception {
		for (ActiveRecordPlugin arp : activeRecordPlugins) {
			arp.stop();
		}
		for (DruidPlugin druidPlugin : druidPlugins) {
			druidPlugin.stop();
		}
		log.info("释放数据源完成.");
	}

	public void afterPropertiesSet() throws Exception {
		log.info("开始解析数据源配置文件.");
		List<DbProperties> databases = parseXmlToList();
		log.info("完成解析数据源配置文件.");
		if (databases != null && databases.size() > 0) {
			log.info("开始加载数据源{}.", databases.size());
			init(databases);
			log.info("完成加载数据源{}.", databases.size());
		} else {
			log.info("无数据源配置.");
		}
	}

	private List<DruidPlugin> druidPlugins = new LinkedList<DruidPlugin>();
	private List<ActiveRecordPlugin> activeRecordPlugins = new LinkedList<ActiveRecordPlugin>();

	public void init(List<DbProperties> databases) {
		DruidPlugin druidPlugin;
		ActiveRecordPlugin arp;
		for (DbProperties bean : databases) {
			if (StrKit.equals("MongoDB", bean.getType())) {
			} else {
				druidPlugin = new DruidPlugin(bean.getUrl(), bean.getUser(), bean.getPassword());
				if (!"sqlite".equalsIgnoreCase(bean.getType()))
					druidPlugin.set(bean.getInitialSize(), bean.getMinIdle(), bean.getMaxActive());
				arp = new ActiveRecordPlugin(bean.getName(), druidPlugin);
				arp.setShowSql(bean.isShowSql());
				arp.setTransactionLevel(Connection.TRANSACTION_READ_COMMITTED);
				arp.getEngine().setToClassPathSourceFactory();
				if (!StrKit.isBlank(bean.getSqlTemplate())) {
					arp.addSqlTemplate(bean.getSqlTemplate());
				}
				if (bean.getMappingClassList() != null && bean.getMappingClassList().size() > 0) {
					for (String mappingClass : bean.getMappingClassList()) {
						ReflectUtil.on(mappingClass).call("mapping", arp);
					}
				}
				if ("oracle".equalsIgnoreCase(bean.getType())) {
					druidPlugin.setDriverClass(bean.getDriverClass());
					arp.setDialect(new OracleDialect());

				} else if ("mysql".equalsIgnoreCase(bean.getType())) {
					arp.setDialect(new MysqlDialect());
				} else if ("sqlserver".equalsIgnoreCase(bean.getType())) {
					arp.setDialect(new SqlServerDialect());
				} else if ("sqlite".equalsIgnoreCase(bean.getType())) {
					druidPlugin.setDriverClass(bean.getDriverClass());
					arp.setDialect(new Sqlite3Dialect());
				} else if ("postgresql".equalsIgnoreCase(bean.getType())) {
					druidPlugin.setDriverClass(bean.getDriverClass());
					arp.setDialect(new PostgreSqlDialect());
				}
				// arp.setContainerFactory(new CaseInsensitiveContainerFactory(true));//false
				// 是大写, true是小写, 不写是区分大小写
				druidPlugin.start();
				arp.start();

				druidPlugins.add(druidPlugin);
				activeRecordPlugins.add(arp);

				JFinalJson.setModelAndRecordFieldNameToCamelCase();
				JFinalJson.setSkipNullValueField(true);

				JFinalJson.setModelAndRecordFieldNameConverter(fieldName -> {
					return StrKit.toCamelCase(fieldName, true);
				});
			}
		}
	}
	
	@SuppressWarnings("unchecked")
	private List<DbProperties> parseXmlToList() {
		List<DbProperties> lists = new LinkedList<DbProperties>();
		try {
			SAXReader reader = new SAXReader();
			 reader.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd",
			 false);
			Document document = null;
			
			if(StrKit.equals("dev", active)) {
				document = reader.read(this.getClass().getResourceAsStream("/springboot-ds.xml"));
			}else {
				document = reader.read(new FileInputStream(path));
			}
			
			Element root = document.getRootElement();
			List<Element> elements = root.elements();

			List<Element> mappingClass;
			DbProperties bean;
			for (Element element : elements) {
				bean = new DbProperties();
				bean.setName(element.elementText("name"));
				bean.setType(element.elementText("type"));
				bean.setUser(element.elementText("user"));
				bean.setPassword(element.elementText("password"));
				bean.setUrl(element.elementText("url"));

				if (element.element("mappingClass") == null) {
					continue;
				}
				
				if (element.element("mappingClass").elements() != null) {
					mappingClass = element.element("mappingClass").elements();
					if (mappingClass != null && mappingClass.size() > 0) {
						List<String> eleList = new LinkedList<String>();
						for (Element ele : mappingClass) {
							eleList.add(ele.getText());
						}
						bean.setMappingClassList(eleList);
					}
				}

				if (!StrKit.isBlank(element.elementText("port"))) {
					bean.setPort(Integer.parseInt(element.elementText("port")));
				}
				if (!StrKit.isBlank(element.elementText("initialSize"))) {
					bean.setInitialSize(Integer.parseInt(element.elementText("initialSize")));
				}
				if (!StrKit.isBlank(element.elementText("minIdle"))) {
					bean.setMinIdle(Integer.parseInt(element.elementText("minIdle")));
				}
				if (!StrKit.isBlank(element.elementText("maxActive"))) {
					bean.setMaxActive(Integer.parseInt(element.elementText("maxActive")));
				}
				if (!StrKit.isBlank(element.elementText("showSql"))) {
					bean.setShowSql(Boolean.parseBoolean(element.elementText("showSql")));
				}
				if (!StrKit.isBlank(element.elementText("driverClass"))) {
					bean.setDriverClass(element.elementText("driverClass"));
				}
				lists.add(bean);
			}
		} catch (DocumentException e) {
			e.printStackTrace();
		} catch (SecurityException e) {
			e.printStackTrace();
		} catch (Exception e) {
			e.printStackTrace();
		}
		return lists;
	}

}
